/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package hbi.wire.java.guest;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 
 * This stream should be written by only one thread.
 * 
 * @author Compl
 * 
 */
public class ByteBufferOutputStream extends OutputStream
{

    public final int CHUNK_SIZE;

    private ByteBuffer current;

    public ByteBufferOutputStream(int chunkSize)
    {
        if (chunkSize <= 0) throw new IllegalArgumentException();
        this.CHUNK_SIZE = chunkSize;

        this.current = ByteBuffer.allocate(chunkSize);
    }

    private final Queue<ByteBuffer> queue = //
    new ConcurrentLinkedQueue<ByteBuffer>();

    public boolean dataAvailable()
    {
        return !this.queue.isEmpty();
    }

    public ByteBuffer pollData()
    {
        return this.queue.poll();
    }

    @Override
    public void flush() throws IOException
    {
        ByteBuffer cur = this.current;

        if (cur.position() <= 0)
        // empty, may just be reallocated by another thread
            return;

        this.current = ByteBuffer.allocate(CHUNK_SIZE);
        cur.flip();
        this.queue.offer(cur);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException
    {
        ByteBuffer cur = this.current;
        for (int amount;;)
        {
            amount = Math.min(len, cur.remaining());
            cur.put(b, off, amount);
            len -= amount;
            if (len <= 0) return;
            off += amount;
            cur = this.current = ByteBuffer.allocate(CHUNK_SIZE);
        }
    }

    @Override
    public void write(byte[] b) throws IOException
    {
        this.write(b, 0, b.length);
    }

    @Override
    public void write(int b) throws IOException
    {
        ByteBuffer cur = this.current;
        for (;;)
        {
            if (cur.remaining() > 0)
            {
                cur.put((byte) b);
                return;
            }
            cur = this.current = ByteBuffer.allocate(CHUNK_SIZE);
        }
    }

    @Override
    public void close() throws IOException
    {
        queue.clear();
    }

}
